#ifndef _TABLE_ATTRIBUTE_VAL_H_
#define _TABLE_ATTRIBUTE_VAL_H_

#include <buildspec.h>
#include <Client/ClientUtils/Network/TableAttributeDesc.h>
#include <exceptions/GSTRuntimeException.h>
#include <Client/ClientUtils/Network/NetworkInterfaceExceptions.hpp>

#include <boost/any.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/ptr_container/ptr_map.hpp>

#include <string>
#include <vector>

namespace GST {
	
	//exceptions:
namespace exceptions {
	class BadCast
		: public GSTRuntimeException
	{
	public:
		BadCast(const std::string & throwLocation, const std::string& msg)
			: GSTRuntimeException(throwLocation,msg)
		{}
	};

	class KeyIsNull
		: public GSTRuntimeException
	{
	public:
		ClientUtils::TableAttributeValPtr key;

		KeyIsNull(const std::string & throwLocation, ClientUtils::TableAttributeValPtr key)
			: GSTRuntimeException(throwLocation,"KeyIsNull")
			, key(key)
		{}
		~KeyIsNull() throw() {}
	};
}

namespace ClientUtils {

	//prototype:
	class AbstractRDBAccess;
    class GRPCAccess;

	/**
	 * A value with a table description attached to it.
	 */
class GST_API_EXPORT TableAttributeVal
	: public boost::any
{	
public:

	///Construct with value ctor
	TableAttributeVal(TableAttributeDescPtr desc, const boost::any& value);
	///Construct with value as string ctor
	TableAttributeVal(TableAttributeDescPtr desc, const std::string& value);
	///Construct without value (NULL) ctor
	TableAttributeVal(TableAttributeDescPtr desc);
	TableAttributeVal(const TableAttributeVal& other);

	TableAttributeVal();
	
	virtual ~TableAttributeVal() { ///@todo: implement me!
	}

	void assign(const boost::any& value);
	void assignNull();
	TableAttributeVal & operator= (const TableAttributeVal & other);
	bool operator== (const TableAttributeVal & other) const;
	/**
	  * compares the internal values (dtype and dimension) and return true if they are equal
	  * @parameter compareDesc optionally compares AttributeName and tablename from description
	  * 
	  */
	bool compareValue(const TableAttributeVal & other, bool compareDesc=false) const;

	bool operator!= (const TableAttributeVal & other) const
	{
		return !(*this == other);
	};
	///@name getters
	//@{
	virtual TableAttributeDescPtr getDesc() const;
	bool isNullValue() const;
	///alias for getValue()
	std::string ToString() const;
	static bool CanCastToString(TableAttributeDesc::DType dtype);
	double ToDouble() const;
	static bool CanCastToDouble(TableAttributeDesc::DType dtype);
	boost::any getBoostAny();

	template <typename T>
	T getAs() const
	{
		if(this->isNullValue()) {
			throw GST::exceptions::IsNull(__METHOD_NAME__, this->getDesc()->getName(), -1);
		}

		try
		{
			return boost::any_cast<T>(*this);
		}
		catch(const boost::bad_any_cast &e)
		{
			throw exceptions::BadCast(__METHOD_NAME__,"Cast problem. Value was set in incompatible type. boost_any_cast message: "+std::string(e.what()));
		}
	}
	//@}

protected:
	TableAttributeDescPtr description;
	
};
typedef boost::shared_ptr<TableAttributeVal> TableAttributeValPtr;

//single record handle
typedef std::map<std::string, TableAttributeValPtr, Utils::UpperCaseCompare> TableAttributeValList;
typedef boost::shared_ptr<TableAttributeValList> TableAttributeValListPtr;
typedef std::vector<TableAttributeValPtr> TableAttributeValueArray;
typedef boost::shared_ptr<TableAttributeValueArray> TableAttributeValueArrayPtr;
/**
 * Maps a feature_id to its attribute values.
 */
typedef std::map<long, TableAttributeValListPtr> ObjectPropertiesByIdgeoMap;
typedef boost::shared_ptr<ObjectPropertiesByIdgeoMap> ObjectPropertiesByIdgeoMapPtr;

typedef std::vector<std::string> NamesList;
typedef boost::shared_ptr<NamesList> NamesListPtr;


//-----------------------------------------------------------------------------------------------------------


/**
 * Table description with value and with key values of constrained property attached to it.
 *
 * This class represents a constrained property value (one value of the mapped column from the referenced table).
 * The corresponding key values are stored in keysval and can be accessed trough getKeyValues() -- which can be a 
 * list of values if the foreign key is consisting of multiple columns.
 *
 * @see TableConstrainedAttributeDesc
 */
class GST_API_EXPORT ConstrainedTableAttributeVal
	: public TableAttributeVal
{	 
public:

	///Construct an empty value (NULL in mapped column)
	ConstrainedTableAttributeVal(TableAttributeDescPtr desc);
	///Construct with existing val and keys values
	ConstrainedTableAttributeVal(TableAttributeVal val, TableAttributeValueArray keys);
	ConstrainedTableAttributeVal();
	ConstrainedTableAttributeVal(TableAttributeVal val);
	virtual ~ConstrainedTableAttributeVal();

	/**
	  * comparison operator 
	  * @returns true if key values, key description and mapped value are equal
	  */
	bool operator == (const ConstrainedTableAttributeVal& op2) const;
	/**
	  * @returns true if the pkValues have equal values like keysval of this object. 
	  * pkValues.getDesc() no need to be equal (so values can be in different tables)
	  * @throws KeyIsNull if one of the pkValues contains a NULL value
	  */
	bool compareKeyValues( const TableAttributeValueArray& pkValues ) const;

	///@name getters
	//@{
	virtual NamesList getKeyValuesNameList() const;
	virtual TableAttributeValueArray getKeyValues() const;
	virtual void addKeyValue(TableAttributeValPtr kv);
//	virtual TableAttributeDescPtr getDesc() const;

	//@}

protected:
	TableAttributeValueArray keysval;
	
};
typedef boost::shared_ptr<ConstrainedTableAttributeVal> ConstrainedTableAttributeValPtr;
typedef std::vector<ConstrainedTableAttributeValPtr> ConstrainedTableAttributeValArray;
typedef boost::shared_ptr<ConstrainedTableAttributeValArray> ConstrainedTableAttributeValArrayPtr;


//-----------------------------------------------------------------------------------------------------------


//table handle
class GST_API_EXPORT Record
{
	friend class AbstractRDBAccess;
    friend class GRPCAccess;
public:
	Record(int columns);
	Record(const TableAttributeValList & values);

	const TableAttributeVal * getValue( int column ) const;
	const TableAttributeVal * getValue( const std::string & columnName ) const;
	void setValue( int column, const boost::any & changedValue );
	void setValue( const std::string & columnName, const boost::any & changedValue );
	void setNull( int column );
	void setNull( const std::string & columnName );
	bool isNull( int column ) const;
	bool isNull( const std::string & columnName ) const;
	size_t size() const;
	
	bool isPrimaryKey( int column ) const;
	bool isPrimaryKey( const std::string & columnName ) const;
	bool isModified( int column ) const;
	bool isModified( const std::string & columnName ) const;
	int resolveColumnByName( const std::string & columnName ) const;

protected:
	std::vector< TableAttributeValPtr > m_data;
	std::vector< bool > m_modified;
	std::vector< boost::any > m_originalvalue;
	typedef std::map<std::string, int, Utils::UpperCaseCompare> NameColMap;
	NameColMap m_NameToCol;

	const boost::any & getOriginalValue( int column ) const;
	void initValue(int column, TableAttributeValPtr val);
};
//typedef std::vector< TableAttributeValPtr > Record;
typedef boost::shared_ptr< Record > RecordPtr;
typedef std::vector< RecordPtr > DataTable;
typedef boost::shared_ptr< DataTable > DataTablePtr;


}// namespace ClientUtils
}// namespace GST


#endif  //_TABLE_ATTRIBUTE_VAL_H_
